﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using DTOTemplatedGenerator.Models;
using DTOTemplatedGenerator.UIInterfaces;
using Newtonsoft.Json;

namespace DTOTemplatedGenerator.ViewModels
{
    public class ViewModel: BaseVM
    {        
        public static ViewModel Instance { get { return _istance; } }

        string _pathToSaveContext;
        string _pathToTemplates;
        string _pathToTypeTrans;
        ObservableCollection<PropertyModel> _props = new ObservableCollection<PropertyModel>();
        ObservableCollection<PropTemplateModel> _propTemplates = new ObservableCollection<PropTemplateModel>();
        private ObservableCollection<TypesTransModel> _typesTrans = new ObservableCollection<TypesTransModel>();
        ObservableCollection<GeneralTemplateModel> _generalTemplates = new ObservableCollection<GeneralTemplateModel>();
        private string _generatedResult;
        private static ViewModel _istance;
        private string _generalTemGenResult;
		private IUserInform _user;

		private RelayCommand _parseClipBoardCode;
		private RelayCommand _parseTabedValuesCommand;
		private RelayCommand _parseCSVCommand;
		private RelayCommand _generateCommand;

		public ViewModel(IUserInform user)
		{
			_user = user;
			_istance = this;
			var asm = Assembly.GetEntryAssembly();
			_pathToSaveContext = asm.Location.Replace(asm.ManifestModule.Name, "Data.data");
			_pathToTemplates = asm.Location.Replace(asm.ManifestModule.Name, "Templates.data");
			_pathToTypeTrans = asm.Location.Replace(asm.ManifestModule.Name, "TypesTransformations.data");

			if (File.Exists(_pathToTemplates))
			{
				var templatesRaw = File.ReadAllText(_pathToTemplates);
				var templates = JsonConvert.DeserializeObject<PropTemplateModel[]>(templatesRaw);
				_propTemplates = new ObservableCollection<PropTemplateModel>(templates);
				File.Delete(_pathToTemplates);
				SaveData();
			}

			if (File.Exists(_pathToTypeTrans))
			{
				var ttRaw = File.ReadAllText(_pathToTypeTrans);
				var tts = JsonConvert.DeserializeObject<TypesTransModel[]>(ttRaw);
				_typesTrans = new ObservableCollection<TypesTransModel>(tts);
				File.Delete(_pathToTypeTrans);
				SaveData();
			}

			if (File.Exists(_pathToSaveContext))
			{
				var raw = File.ReadAllText(_pathToSaveContext);
				var sc = JsonConvert.DeserializeObject<SaveCotext>(raw);
				if (sc.Templates != null)
					_propTemplates = new ObservableCollection<PropTemplateModel>(sc.Templates.OrderBy(pt => pt.Category));
				if (sc.Templates != null)
					_typesTrans = new ObservableCollection<TypesTransModel>(sc.TypesTrans);
				if (sc.GeneralTemplates != null)
					_generalTemplates = sc.GeneralTemplates;
			}

			_parseClipBoardCode = new RelayCommand(ParseCode);
			_parseTabedValuesCommand = new RelayCommand(ParseTabedValues);
			_parseCSVCommand = new RelayCommand(ParseCSV);
			_generateCommand = new RelayCommand(() => Generate());
		}

        public ObservableCollection<PropertyModel> Props { get { return _props; } }        

        public ObservableCollection<PropTemplateModel> PropTemplates { get { return _propTemplates; } }

        public ObservableCollection<TypesTransModel> TypesTrans { get { return _typesTrans; } }

        public ObservableCollection<GeneralTemplateModel> GeneralTemplates { get { return _generalTemplates; } }

        public Func<List<PropertyModel>> GetSelectedProps { get; set; }

		public RelayCommand ParseClipBoardCode
		{
			get { return _parseClipBoardCode; }
		}

		public RelayCommand ParseTabedValuesCommand
		{
			get { return _parseTabedValuesCommand; }
		}

		public RelayCommand ParseCSVCommand
		{
			get { return _parseCSVCommand; }
		}

		public RelayCommand GenerateCommand
		{
			get { return _generateCommand; }
		}

        public string GeneratedResult { 
            get { return _generatedResult; } 
            set {
				if (_generatedResult != value)
				{
					_generatedResult = value;
					OnPropertyChanged("GeneratedResult");
				}
            } 
        }

        public string GeneralTemGenResult
        {
            get { return _generalTemGenResult; } 
            set {
				if (_generalTemGenResult != value)
				{
					_generalTemGenResult = value;
					OnPropertyChanged("GeneralTemGenResult");
				}
            } 
        }

        public void ParseCode()
        {
			string clipBoard = _user.GetClipboardText();

			if(string.IsNullOrWhiteSpace(clipBoard))
			{
				_user.Inform("Clip board does not contain text");
				return;
			}

            var orgCount = _props.Count;
			var lines = clipBoard.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);            
            foreach (var line in lines)
            { 
                var propRaw = line;
                if (!propRaw.Contains("public"))
                    continue;
                var prop = new PropertyModel();
                if (!propRaw.Contains("{"))
                {
                    prop.IsField = true;
                    propRaw = propRaw.Replace(";", "");
                }
                else
                    propRaw = propRaw.Remove(propRaw.IndexOf('{'));
                
                var words = propRaw.Split(default(string[]), StringSplitOptions.RemoveEmptyEntries);
                if (words.Length == 3)//public TypeName PropName
                {
                    prop.Type = words[1];
                    prop.Name = words[2];

                    _props.Add(prop);
                }
            }
			if (_props.Count == orgCount)
				_user.Inform("Failed to parse C# code from your Clipboard");
        }

        internal void ParseTabedValues()
        {
			string clipBoard = _user.GetClipboardText();

			if (string.IsNullOrWhiteSpace(clipBoard))
			{
				_user.Inform("Clip board does not contain text");
				return;
			}

            var orgCount = _props.Count;
			ParsTabbedText(clipBoard);
			if (_props.Count == orgCount)
				_user.Inform("Failed to parse spaced values from your Clipboard");
        }

		private void ParsTabbedText(string clipBoard)
		{
			var lines = clipBoard.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
			foreach (var line in lines)
			{
				var propRaw = line;
				var prop = new PropertyModel();

				var words = propRaw.Split(new[] { "\t" }, StringSplitOptions.RemoveEmptyEntries);
				if (words.Length > 0)
					prop.Name = words[0];
				if (words.Length > 1)
					prop.Type = words[1];
				_props.Add(prop);
			}
		}

        internal void ParseCSV()
        {
			string clipBoard = _user.GetClipboardText();

			if (string.IsNullOrWhiteSpace(clipBoard))
			{
				_user.Inform("Clip board does not contain text");
				return;
			}

			var orgCount = _props.Count;
			clipBoard = clipBoard.Replace(",", " ");
			ParsTabbedText(clipBoard);

			if (_props.Count == orgCount)
				_user.Inform("Failed to parse spaced values from your Clipboard");
        }

        internal void SetTemplateToProps(PropTemplateModel templateModel)
        {
            GetSelectedProps().ForEach(p => p.Template = templateModel);
        }        
    
        internal void SaveTemplates()
        {
            if(_propTemplates.Count == 0)
                return;
            File.WriteAllText(_pathToTemplates, JsonConvert.SerializeObject(_propTemplates.ToArray()));
        }

        internal void SaveTypesTransformations()
        {
            if (_typesTrans.Count == 0)
                return;
            File.WriteAllText(_pathToTypeTrans, JsonConvert.SerializeObject(_typesTrans.ToArray()));
        }

        public void Generate(IEnumerable<PropertyModel> particular = null)
        {
            var sb = new StringBuilder();
            foreach (var p in particular ?? GetSelectedProps())
            {
                if (p.Template == null)
                    continue;
                var gen = p.Template.Text.Replace("@PropName", p.Name);
                //var type = p.Type;
                //if (p.Template.TypesMatch != null)
                //{
                //    var typesTrans = _typesTrans.FirstOrDefault(tt => tt.Name == p.Template.TypesMatch);
                //    if (typesTrans != null)
                //    {
                //        var match = typesTrans.Matchs.FirstOrDefault(tm => tm.Source == type);
                //        if(match != null)
                //            type = match.Match;
                //    }
                //}
                if(p.TransType != null)
                    gen = gen.Replace("@PropType", p.TransType);                
                if(p.Type != null)                    
                    gen = gen.Replace("@OrgPropType", p.Type);
                //insert Aux info
                if (p.Attr != null)                
                    gen = gen.Replace("@Attr", p.Attr);
                
                sb.AppendLine(gen);
            }
            GeneratedResult = sb.ToString();
        }

        internal void GenerateGeneralTemplate(GeneralTemplateModel gtm)
        {
            var propGenResultParam = "@GenerateProps";
            var sb = new StringBuilder(gtm.Template);
            foreach (var d in gtm.Data)
            {
                sb.Replace("@" + d.Key, d.Value);
            }
            while (sb.ToString().Contains(propGenResultParam))
            {
                var tokenInd = sb.ToString().IndexOf(propGenResultParam);
                var openBrace = tokenInd  + propGenResultParam.Length + 1;
                var closeBrace = sb.ToString().IndexOf(")", openBrace);
                var propTempalateName = sb.ToString().Substring(openBrace, closeBrace - openBrace);

                var propTemplate = PropTemplates.FirstOrDefault(pt => pt.Name != null && pt.Name.Trim() == propTempalateName.Trim());
                if (propTemplate != null)
                {
                    foreach (var p in this.Props)
                        p.Template = propTemplate;
                    Generate(this._props);
                    sb.Remove(tokenInd, closeBrace - tokenInd + 1);
                    sb.Insert(tokenInd, ViewModel.Instance.GeneratedResult);
                }
                else
                    sb.Remove(tokenInd, closeBrace - tokenInd + 1);                
            }            
            ViewModel.Instance.GeneralTemGenResult = sb.ToString();
        }

        internal void RemoveProps()
        {
            var props = GetSelectedProps();
            foreach (var p in props)
                _props.Remove(p);
        }

        internal void SetTypeTransToTemplates(List<PropTemplateModel> list, TypesTransModel typesTransModel)
        {
            list.ForEach(t => t.TypesMatch = typesTransModel.Name);
        }

        internal void RemoveTemplates(List<PropTemplateModel> list)
        {
            list.ForEach(t => _propTemplates.Remove(t));
        }

        internal void RemoveTypesTransformations(List<TypesTransModel> list)
        {
            list.ForEach(tt => _typesTrans.Remove(tt));
        }

        internal void SaveData()
        {
            var sc = new SaveCotext 
            { 
                Templates = PropTemplates, 
                TypesTrans = TypesTrans, 
                GeneralTemplates = GeneralTemplates 
            };
            var raw = JsonConvert.SerializeObject(sc);
            File.WriteAllText(_pathToSaveContext, raw);
        }

        internal void ImportData(string raw, Func<bool> askToReplace)
        {
            bool? replaceMatch = null;
            //var raw = File.ReadAllText(fileName);
            var sc = JsonConvert.DeserializeObject<SaveCotext>(raw);
            if(sc.Templates != null)
            {
                foreach(var tem in sc.Templates)
                {
                    var exist = PropTemplates.FirstOrDefault(t => t.Name == tem.Name);
                    if (exist != null)
                    {
                        if (replaceMatch == null)
                            replaceMatch = askToReplace();
                        if (replaceMatch == true)
                        {
                            _propTemplates.Remove(exist);
                            _propTemplates.Add(tem);
                        }
                    }
                    else
                        _propTemplates.Add(tem);
                }
            }
            if (sc.TypesTrans != null)
            {
                foreach (var tt in sc.TypesTrans)
                {
                    var exist = TypesTrans.FirstOrDefault(t => t.Name == tt.Name);
                    if (exist != null)
                    {
                        if (replaceMatch == null)
                            replaceMatch = askToReplace();
                        if (replaceMatch == true)
                        {
                            TypesTrans.Remove(exist);
                            TypesTrans.Add(tt);
                        }
                    }
                    else
                        TypesTrans.Add(tt);
                }
            }

            if (sc.GeneralTemplates != null)
            {
                foreach (var gt in sc.GeneralTemplates)
                {
                    var exist = GeneralTemplates.FirstOrDefault(t => t.Template == gt.Template);
                    if (exist != null)
                    {
                        if (replaceMatch == null)
                            replaceMatch = askToReplace();
                        if (replaceMatch == true)
                        {
                            GeneralTemplates.Remove(exist);
                            GeneralTemplates.Add(gt);
                        }
                    }
                    else
                        GeneralTemplates.Add(gt);
                }
            }
        }

        internal void ExportData()
        {
            var sc = new SaveCotext { Templates = PropTemplates, TypesTrans = TypesTrans, GeneralTemplates = GeneralTemplates };
            var raw = JsonConvert.SerializeObject(sc);

			_user.SetClipboardText(raw);
        }

    }
}
